#
# Package.
#

package iSOCK::Controller::Specification::Process;

#
# Parent.
#

use Moose;
use namespace::autoclean;

BEGIN { extends 'iSOCK::Controller::Root' }

#
# Uses.
#

use Date::Calc qw( Today_and_Now Delta_DHMS );
use SOCK::BOT qw( last_breath );
use SOCK::LOG;
use Try::Tiny;

#
# Documentation.
#

=head1 NAME

iSOCK::Controller::Product - Product Controller for iSOCK

=head1 DESCRIPTION

[enter your description here]

=head1 METHODS

=cut

#
# Subs.
#

sub index :Path :Args(0) {
  my ( $self, $c ) = @_;

  #
  # Login.
  #

  unless ( $c->user ) {
    $self->bounce_to ( $c => '/login' );
  }
  
  #
  # Core.
  #
  
  my $parameters = $c->request->parameters;
  my $app_m      = $c->model ( 'App' );

  if ( $c->request->method eq 'POST' && $parameters->{form_product_submit} ) {
    #
    # Session.
    #

    my $session                 = $c->session;
    my $session_id              = $c->sessionid;
    my $session_path            = $self->session_path ( $c );
    my $session_started         = [ Today_and_Now () ];

    $session->{session_started} = $session_started;
    $session->{session_done}    = [];
    $session->{session_dhms}    = [];

    #
    # Busy.
    #

    $app_m->alter_session ({ c => $c, busy => 1 });

    #
    # Log.
    #

    my $log = SOCK::LOG->new ({ file => "$session_path/isock_specification.log", fatality => 0 });

    my $info = $app_m->info_string ( $c );

    $c->log->info ( "[Specification::Process::go] BEGIN ( $info ; T: " . $self->SUPER::_isoish ( @{$session->{session_started}} ) . " ) {" );

    #
    # Verbosity.
    #

    $log->say_n_right ( "Processing within $session_id session..." );

    #
    # Cleanup.
    #

    delete $session->{$_} for grep { ! $parameters->{$_} } qw( xls_file mif_file );

    unlink grep { not m/isock_specification/ } glob "$session_path/*.log";

    for ( map { $c->model ( $_ ) } qw( Component Product ) ) {
      $_->delete_session_files ({ c => $c });
      $_->delete_session_keys  ({ c => $c, reason => 'cleanup prior to specification processing' });
    }

    #
    # Variables.
    #

    my $svn_product                   = ( !defined $parameters->{svn_product} || $parameters->{svn_product} eq '-' ) ? undef : $parameters->{svn_product};
    my $svn_branch                    = ( !defined $parameters->{svn_branch}  || $parameters->{svn_branch}  eq '-' ) ? undef : $parameters->{svn_branch};
    my $svn_tag                       = ( !defined $parameters->{svn_tag}     || $parameters->{svn_tag}     eq '-' ) ? undef : $parameters->{svn_tag};

    my $do_spirits                    = $parameters->{do_spirits};
    my $do_mifs                       = $parameters->{do_mifs};

    my $process_followup              = grep { $parameters->{"do_$_"} } qw( spirits mifs );
    my $process_specification         = grep { $parameters->{"do_$_"} } qw( diagram abstract api verilog_pios tcl annotated_mifs pio_sheet followup );

    $session->{last_svn_product}      = $session->{svn_product};
    $session->{svn_product}           = $svn_product;
    $session->{svn_branch}            = $svn_branch;
    $session->{svn_tag}               = $svn_tag;
    $session->{process_followup}      = $process_followup;
    $session->{process_specification} = $process_specification;

    #
    # Specification.
    #

    my $ko;

    try {
      my $model  = $c->model ( 'Specification' );
      my $do_svn = $svn_product && $parameters->{do_svn_mif_file};

      $model->delete_session_files ({ c => $c });

      my ( $not_asked_for, $not_ok );

      if ( $process_specification && ( $parameters->{mif_file} || $do_svn ) ) {
        my $specification_file;

        unless ( $do_svn ) {
          unless ( $specification_file = $self->upload ( $c, 'mif_file', $log, 'mif', 'design specification document' ) ) {
            $not_ok = 1;
          }
        }

        unless ( $not_ok ) {
          if ( $model->to_spirit ({ c => $c, specification_file => $specification_file, log => $log }) ) {
            $c->session->{svn_product_revision} = $c->model ( 'Product' )->repository_revision ({ c => $c });
          }
          else {
            $not_ok = 1;
          }
        }

        if ( $not_ok && $process_followup ) {
          $log->yell ( "Cancelled requested follow-up processing due to specification problems." );

          undef $process_followup;
        }
      }
      else {
        $not_asked_for = 1;
      }

      $model->delete_session_keys ({ c => $c, reason => 'specification was not requested or is not OK' }) if $not_asked_for || $not_ok;

      $ko ||= $not_ok;
    }
    catch {
      $log->yell ( "Something went utterly wrong while processing specification (please contact SOCK support):" );
      $log->mumble ( last_breath ( $_ ) );

      $ko = 1;
    };

    #
    # Follow-up.
    #

    try {
      my $model  = $c->model ( 'Followup' );
      my $do_svn = $svn_product && $parameters->{do_svn_xls_file};

      $model->setup_session_directories ({ c => $c });
      $model->delete_session_files      ({ c => $c });

      my ( $not_asked_for, $not_ok );

      if ( $process_followup && ( $parameters->{xls_file} || $do_svn ) ) {
        my $followup_file;

        unless ( $do_svn ) {
          unless ( $followup_file = $self->upload ( $c, 'xls_file', $log, 'xls' ) ) {
            $not_ok = 1;
          }
        }

        unless ( $not_ok ) {
          if ( $model->to_whatever ({
            c                 => $c,
            followup_file     => $followup_file,
            svn               => $parameters->{datasheets_source} eq 'Subversion',
            svn_co_from_tags  => $parameters->{datasheets_source} eq 'Subversion (tags)',
            svn_co_from_trunk => $parameters->{datasheets_source} eq 'Subversion (trunk)',
            log               => $log,
          }) ) {
            $c->session->{svn_component_revision} = $c->model ( 'Component' )->repository_revision ({ c => $c });
          }
          else {
            $not_ok = 1;
          }
        }
      }
      else {
        $not_asked_for = 1;
      }

      $model->delete_session_keys ({ c => $c, reason => 'follow-up was not requested or is not OK' }) if $not_asked_for || $not_ok;

      $ko ||= $not_ok;
    }
    catch {
      $log->yell ( "Something went utterly wrong while processing follow-up (please contact SOCK support):" );
      $log->mumble ( last_breath ( $_ ) );

      $ko = 1;
    };

    #
    # Arkham asylum.
    #

    for ( qw( vendor library name version ) ) {
      if ( $session->{"followup_product_$_"} && $session->{"specification_product_$_"} ) {
        if ( $session->{"followup_product_$_"} ne $session->{"specification_product_$_"} ) {
          $log->counsel ( "Discrepancy between follow-up $_ ('" . $session->{"followup_product_$_"} . "') and specification $_ ('" . $session->{"specification_product_$_"} . "')." );

          $session->{"product_$_"} = $session->{"specification_product_$_"};
        }
        else {
          $session->{"product_$_"} = $session->{"followup_product_$_"};
        }
      }
      else {
        $session->{"product_$_"} = $session->{"followup_product_$_"} || $session->{"specification_product_$_"};
      }
    }

    #
    # Nuke leftovers.
    #

    if ( $process_followup && ! $process_specification ) {
      if ( $c->model ( 'Specification' )->delete_session_files ({ c => $c }) ) {
        $log->counsel ( "Unlinked leftover specification related files." );
      }
    }

    if ( $process_specification && ! $process_followup ) {
      if ( $c->model ( 'Followup' )->delete_session_files ({ c => $c }) ) {
        $log->counsel ( "Unlinked leftover follow-up related files." );
      }
    }

    #
    # Payload.
    #

    my $spirit_from_specification = $session->{mif_file} && $session->{spirit_file};
    my $spirits_from_followup     = $session->{xls_file} && $do_mifs && $do_spirits;

    if ( $ko ) {
      my $debrief = sub { _debrief_spirit_to_whatever ( $log, $parameters, $spirit_from_specification, $spirits_from_followup, @_ ) };

      $debrief->( qw( do_abstract do_api do_annotated_mifs do_diagram do_followup do_pio_sheet do_verilog_pios ) );

      delete $session->{$_} for qw( xls_file mif_file );
    }
    else {
      try {
        my $model = $c->model ( 'Product' );
        my $c_log = { c => $c, log => $log };

        if ( $spirit_from_specification && $spirits_from_followup ) {
          $model->to_abstract         ( $c_log ) if $parameters->{do_abstract};
          $model->to_api              ( $c_log ) if $parameters->{do_api};
          $model->annotate_datasheets ( $c_log ) if $parameters->{do_annotated_mifs};
        }
        if ( $spirit_from_specification ) {
          $model->to_diagram          ( $c_log ) if $parameters->{do_diagram} && !$session->{diagram_file};
          $model->to_followup         ( $c_log ) if $parameters->{do_followup};
          $model->to_pio_sheet        ( $c_log ) if $parameters->{do_pio_sheet};
          $model->to_verilog_pios     ( $c_log ) if $parameters->{do_verilog_pios};
          $model->to_tcl              ( $c_log ) if $parameters->{do_tcl} && !$parameters->{do_api};
        }
      }
      catch {
        $log->yell ( "Something went utterly wrong while generating SPIRIT spin-offs (please contact SOCK support):" );
        $log->mumble ( last_breath ( $_ ) );

        $ko = 1;
      };
    }

    #
    # Verbosity.
    #

    $log->left_n_say ( "Done." );

    #
    # Elapsed.
    #

    my $session_done         = [ Today_and_Now () ];

    $session->{session_done} = $session_done;
    $session->{session_dhms} = [ Delta_DHMS ( @$session_started, @$session_done ) ];

    $c->log->info ( "[Specification::Process::go] } END ( $info ; T: " . $self->SUPER::_isoish ( @$session_done ) . " )" );

    $log->close;

    $session->{ko} = $ko;

    #
    # Mail.
    #

    if ( $parameters->{do_send_mail} ) {
      $c->model ( 'Mail' )->done ({ with => 'product', c => $c });
    }

    #
    # Free.
    #

    $app_m->alter_session ({ c => $c, busy => 0 });

    #
    # Redirect.
    #

    if ( $ko ) {
      $c->response->redirect ( $c->uri_for ( '/logs' ) );
    }
    else {
      $c->response->redirect ( $c->uri_for ( $session->{xls_file} ? '/specification/status' : '/specification/download' ) );
    }

    $c->detach;
  }
  else {
    my @sources = ( 'Subversion', 'Subversion (tags)', 'Subversion (trunk)' );
    
    if ( my $user_r = $app_m->user_r ( $c ) ) {
      @sources = $user_r->datasheet_sources;
    }
    
    $c->stash (
      sources        => \@sources,
      specifications => $app_m->specifications_loh ( $c ),
    );

    $c->forward ( qw( iSOCK::Controller::Root render_view ) );
  }
}

sub _debrief_spirit_to_whatever : Private {
  my ( $log, $parameters, $spirit_from_specification, $spirits_from_followup, @dos ) = @_;
  
  my $s_and_p = {
   do_abstract       => 'An abstract',
   do_api            => 'API files',
   do_annotated_mifs => 'Annotated MIFs',
  };
  my $s_only = {
   do_diagram        => 'A diagram',
   do_followup       => 'A follow-up template sheet',
   do_pio_sheet      => 'A PIO sheet',
   do_verilog_pios   => 'A verilog for PIOs',
  };
  
  for my $do ( @dos ) {
    if ( my $whatever = $s_and_p->{$do} ) {
      $log->yell ( "$whatever was requested but product specification is flawed." ) if $parameters->{$do} && !$spirit_from_specification;
      $log->yell ( "$whatever was requested but product follow-up is flawed."     ) if $parameters->{$do} && !$spirits_from_followup;
    }
    if ( my $whatever = $s_only->{$do} ) {
      $log->yell ( "$whatever was requested but product specification is flawed." ) if $parameters->{$do} && !$spirit_from_specification;
    }
  }
}

#
# Documentation.
#

=head1 AUTHOR

Xavier Caron

=head1 LICENSE

This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut

#
# End of iSOCK::Controller::Specification::Process.
#

1;
